# Copyright (c) 2007, Enrico Franchi
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the University of California, Berkeley nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



import re
import weakref
import configuration

import notifications
import linguistics

from port_command import command, targets
from decorators import with_autorelease_pool

from Foundation import NSObject, NSNotificationCenter, NSNotification

class PerformActionBase(object):
    def __init__(self, port=None, variants=None, c=None):
        self.setup(port, variants=variants, c=c)
        
    def setup(self, port, variants=None, version=None, c=None):
        if not variants:
            variants = []
        self.variants = variants
        self.version = version
        self.port = port
        self.write_action = True
        if c is None:
            self.port_command = command.AuthorizedPortCommand(
                configuration.port_path(),
                catcher= weakref.proxy(self)
            )
        else:
            self.port_command = c        
    
    def add(self, message):
        print message
        if message.startswith('Error:'):
            self.notifyError(message)
            return
        rex = re.compile(r'--->  (\S+)')
        match = rex.match(message)
        if match:
            stage = match.group(1).lower()
            try:
                action = getattr(self, 'on_' + stage)
            except AttributeError:
                pass
            else:
                action()
            self.notify(stage)

    
    def notify(self, stage):
        pass
    
    def notifyError(self, message):
        pass
    
    
class PerformAction(NSObject, PerformActionBase):
    def initWithPortName_withVariants_andVersion(self, port, variants, version):
        self.setup(port, variants, version)
        return self
    
    def notify(self, stage):
        nc = NSNotificationCenter.defaultCenter()
        notification = NSNotification.notificationWithName_object_userInfo_(
            'perform_action',
            self,
            {
                'action'  : self.action,
                'port'    : self.port,
                'version' : self.version,
                'variants': self.variants,
                'stage'   : stage
            }
        )
        nc.postNotification_(notification)
        
    def notifyError(self, message):
        nc = NSNotificationCenter.defaultCenter()
        notification = NSNotification.notificationWithName_object_userInfo_(
            'error',
            self,
            {
                'title'    : u'%s Error' % self.action.capitalize(),
                'message'  : message
            }
        )
        nc.postNotification_(notification)
        
    def finished(self):
        if self.action == targets.INSTALL:
            notifications.askRefreshModel(linguistics.INSTALLED_TAG, self)
        elif self.action == targets.UPGRADE:
            notifications.askRefreshModel(linguistics.OUTDATED_TAG, self)
        elif self.action == targets.SYNC:
            notifications.askRefreshModel(linguistics.OUTDATED_TAG, self)
            notifications.askRefreshModel(linguistics.AVAILABLE_TAG, self)
        elif self.action == targets.UNINSTALL:
            notifications.askRefreshModel(linguistics.INSTALLED_TAG, self)
            notifications.askRefreshModel(linguistics.OUTDATED_TAG, self)
        
    @with_autorelease_pool
    def __call__(self):
        params = []
        if self.version: 
            params.append("@%s" % self.version)
        for variant in self.variants:
            params.append("+%s" % variant)
        getattr(self.port_command, self.action)(self.port, *params)
        self.finished()


_gen_classes = {}

def _verbToNoun(action):
    if    action == targets.INSTALL:
        return 'Installation'
    elif action == targets.UNINSTALL:
        return 'Uninstallation'
    elif action == targets.UPGRADE:
        return 'Upgrade'

def longActionFactory(action, port, variants=None, version=None):
    if variants is None:
        variants = []    
    # this is necessary because ObjectiveC does not allow
    # recreating the very same class.
    try: 
        ActionTask = _gen_classes[action]    
    except KeyError:
        ActionTask = type(
            _verbToNoun(action) + 'Action', 
            (PerformAction, ), 
            {'action': action}
        )
        _gen_classes[action] = ActionTask
    return ActionTask.alloc().initWithPortName_withVariants_andVersion(
        port, variants, version
    )

